package com.hengpeng.itfin.domain.imprest.alipay;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.hengpeng.common.exception.ServiceResult;
import com.hengpeng.common.util.WebClientUtil;
import com.hengpeng.itfin.domain.imprest.AbstractImprestProcessor;
import com.hengpeng.itfin.exception.PaymentException;
import com.hengpeng.itfin.service.ServiceFactory;
import com.hengpeng.itfin.tools.Md5Encrypt;
import com.hengpeng.itfinbase.constants.PaymentConstants;
import com.hengpeng.itfinbase.message.partner.NotifyRequestMsg;
import com.hengpeng.itfinbase.message.partner.NotifyResponseMsg;
import com.hengpeng.itfinbase.message.partner.PayRequestMsg;
import com.hengpeng.itfinbase.message.partner.PayResponseMsg;
import com.hengpeng.itfinbase.persist.Payment;
import com.hengpeng.itfinbase.persist.Payment.PayStatus;
import com.hengpeng.itfinbase.persist.PaymentProvider;

/**
 * <pre>
 * 支付宝支付
 * 单位：元
 * </pre>
 * @author shanguogang
 * @version 1.0, 2015-8-17
 */
public class AlipayImprestProcessor extends AbstractImprestProcessor
{

	private static final Log logger = LogFactory.getLog(AlipayImprestProcessor.class);

	private static final String ALIPAY_CONFIG_SELLEREMAIL = "seller_email";

	private static final String ALIPAY_CONFIG_SUBJECT = "subject";

	private static final String ALIPAY_CONFIG_PRIVATEKEYID = "privateKeyId";

	private String seller_email;

	private String subject;

	private String privateKeyId;

	public AlipayImprestProcessor(ServiceFactory serviceFactory, PaymentProvider provider)
	{
		super(serviceFactory, provider);
		seller_email = extConfigs.get(ALIPAY_CONFIG_SELLEREMAIL);
		subject = extConfigs.get(ALIPAY_CONFIG_SUBJECT);
		privateKeyId = extConfigs.get(ALIPAY_CONFIG_PRIVATEKEYID);
	}

	@Override
	public void payNotify(NotifyRequestMsg request, NotifyResponseMsg response)
	{
		Map<String, String> parameter = request.getParameter();
		String notify_id = parameter.get("notify_id");
		String out_trade_no = parameter.get("out_trade_no");
		String trade_status = parameter.get("trade_status");
		String total_fee = parameter.get("total_fee");
		String sign = parameter.get("sign");
		Double doubleMoney = Double.parseDouble(total_fee) * 100;
		if (checkDigest(parameter, sign))
		{// 验证签名成功

			// 获取支付宝ATN返回结果，true是正确的订单信息，false 是无效的
			String queryAction = "http://notify.alipay.com/trade/notify_query.do?";
			queryAction = queryAction + "&partner=" + this.provider.getMerchantId() + "&notify_id=" + notify_id;
			String responseTxt = checkUrl(queryAction);

			if (responseTxt.equals("true"))
			{
				if (trade_status.equalsIgnoreCase("TRADE_FINISHED") || trade_status.equalsIgnoreCase("TRADE_SUCCESS"))
				{
					response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_SUCCESS);
					response.setRespDesc(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRMSG_SUCCESS);
					ServiceResult<Payment> result = serviceFactory.getPayService().dealPayResult(
							request.getProviderId(), out_trade_no, doubleMoney.longValue(), PayStatus.SUCCESS, "支付成功");
					if (!result.getIsSuccess())
					{
						response.setRespCode(result.getCode());
						response.setRespDesc(result.getMessage());
					}
					else
					{
						response.setMerchantId(this.provider.getMerchantId());
						response.setTransAmt(total_fee);
						response.setOrdId(out_trade_no);
					}
				}
				else
				{
					response.setRespDesc("交易失败，具体信息请登录银行交易管理平台查询。");
					response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_PROVIDERERROR);
					// logger.info("支付失败");
					ServiceResult<Payment> result = serviceFactory.getPayService().dealPayResult(
							response.getProviderId(), response.getOrdId(), doubleMoney.longValue(), PayStatus.FAILED,
							response.getRespDesc());
					if (!result.getIsSuccess())
					{
						response.setRespCode(result.getCode());
						response.setRespDesc(result.getMessage());
					}
				}
			}
			else
			{
				response.setRespDesc("交易失败，具体信息请登录银行交易管理平台查询。");
				response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_PROVIDERERROR);
			}
		}
		else
		{

			response.setRespDesc("签名验证失败，具体信息请登录银行交易管理平台查询。");
			response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_PROVIDERERROR);

		}

	}

	@Override
	public void payRequest(PayRequestMsg request, PayResponseMsg response)
	{

		String defaultbank = "";// 直连银行代码，默认为空
		String token = "";
		String merchantId = provider.getMerchantId();
		Map<String, String> requestMap = request.getOtherParams();
		if (requestMap != null)
		{
			if (requestMap.get("defaultbank") != null)
			{
				defaultbank = requestMap.get("defaultbank").toString();
			}
			if (requestMap.get("token") != null)
			{
				token = requestMap.get("token").toString();
			}
		}

		String orderId = request.getOrdId();
		Map<String, String> postValues = new HashMap<String, String>();

		postValues.put("service", "create_direct_pay_by_user");
		postValues.put("partner", merchantId);
		// postValues.put("_input_charset", "GBK");
		postValues.put("out_trade_no", orderId);
		postValues.put("total_fee", String.valueOf(Double.parseDouble(request.getTransAmt())));
		postValues.put("seller_email", seller_email);
		postValues.put("payment_type", "1");
		postValues.put("subject", subject);
		if (request.getRetUrl() != null && request.getRetUrl().trim() != "")
		{
			page_return_url = request.getRetUrl();
		}
		if (request.getBgRetUrl() != null && request.getBgRetUrl().trim() != "")
		{
			service_notify_url = request.getBgRetUrl();
		}
		postValues.put("return_url", page_return_url);
		postValues.put("notify_url", service_notify_url);
		postValues.put("sign_type", "MD5");
		postValues.put("defaultbank", defaultbank);
		postValues.put("token", token);
		postValues.put("anti_phishing_key", query_timestamp());
		postValues.put("exter_invoke_ip", request.getUserIp());
		if ("1009".equals(this.provider.getId()))
		{
			// 兼容支付宝扫码支付方式,扫码支付方式需要多加一个qr_pay_mode参数
			postValues.put("qr_pay_mode", "2");
		}
		try
		{// 签名
			postValues.put("sign", encodeDigest(postValues));
		}
		catch (PaymentException e)
		{
			response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_DIGESTERROR);

			response.setRespDesc(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRMSG_DIGESTERROR);
			return;
		}

		String htmlString = null;

		htmlString = WebClientUtil.autoSubmitHtmlForm(PaymentConstants.PORTAL_RESPONSE_DEFAULT_ENCODING, "网上支付",
				provider.getPayUrl(), postValues, "POST");
		logger.info("支付商[" + this.provider.getId() + "]类型[" + this.provider.getGatewayType().getText()
				+ "]生成的支付表单内容为：\n" + htmlString);

		response.setOrderFormContent(htmlString);
		response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_SUCCESS);
		response.setRespDesc(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRMSG_SUCCESS);
		response.setMerchantId(merchantId);
		response.setOrdId(orderId);
	}

	private String encodeDigest(Map<String, String> parameters) throws PaymentException
	{
		/**
		 * 加密顺序#service#partner#subject#out_trade_no#total_fee#payment_type#
		 * seller_email#return_url#notify_url#_input_charset#defaultbank#token#anti_phishing_key#exter_invoke_ip
		 **/
		String encodeSrcLink = "#service#partner#subject#out_trade_no#total_fee#payment_type#seller_email#return_url#notify_url#_input_charset#defaultbank#token#anti_phishing_key#exter_invoke_ip";
		if ("1009".equals(this.provider.getId()))
		{
			/**
			 * 支付宝扫码支付方式需要多加一个qr_pay_mode参数
			 */
			encodeSrcLink += "#qr_pay_mode";
		}
		LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
		String[] configs = encodeSrcLink.split("#");
		for (int i = 1; i < configs.length; i++)
		{
			String parameterName = configs[i];
			if (parameterName.trim().length() > 0)
			{
				map.put(parameterName, parameters.get(parameterName.trim()));
			}
		}
		map.put("_input_charset", "GBK");
		String prestr = createLinkString(map); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		prestr = prestr + privateKeyId; // 把拼接后的字符串再与安全校验码直接连接起来
		String mysign = Md5Encrypt.md5(prestr);
		return mysign;
	}

	/**
	 * 由于支付宝的页面返回结果与服务器返回参数不一致，本框架暂不支持，所以此处签名校验无效
	 */
	private boolean checkDigest(Map<String, String> parameters, String signCode)
	{
		// 由于支付宝的页面返回结果与服务器返回参数不一致，本框架暂不支持，所以签名校验无效

		String decodeSrcLink = "#is_success#sign_type#sign#out_trade_no#subject#payment_type#exterface#trade_no#trade_status#notify_id#notify_time#notify_type#seller_email#buyer_email#seller_id#buyer_id#total_fee#body#extra_common_param#gmt_create#gmt_payment#gmt_close#refund_status#gmt_refund#price#quantity#discount#is_total_fee_adjust#use_coupon#";
		LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
		String[] configs = decodeSrcLink.split("#");
		for (int i = 1; i < configs.length; i++)
		{
			String parameterName = configs[i];
			if (parameterName.trim().length() > 0)
			{
				map.put(parameterName, parameters.get(parameterName.trim()));
			}
		}
		String prestr = createLinkString(map); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		prestr = prestr + privateKeyId; // 把拼接后的字符串再与安全校验码直接连接起来
		String mysign = Md5Encrypt.md5(prestr);

		if (signCode.equals(mysign))
		{
			logger.info("交易签名验证成功");
			return true;
		}
		else
		{
			logger.warn("交易签名验证不一致,本地生成摘要字符串[" + mysign + "], 原支付宝返回字符串[" + signCode + "]");
			return true;
		}

	}

	/**
	 * 功能：用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数 注意：远程解析XML出错，与服务器是否支持SSL等配置有关
	 * @param partner 合作身份者ID
	 * @return 时间戳字符串
	 * @throws IOException
	 * @throws DocumentException
	 * @throws MalformedURLException
	 */
	@SuppressWarnings("unchecked")
	private String query_timestamp()
	{
		StringBuffer buf1 = new StringBuffer();
		try
		{
			String strUrl = "https://mapi.alipay.com/gateway.do?service=query_timestamp&partner="
					+ this.provider.getMerchantId();

			SAXReader reader = new SAXReader();
			Document doc = reader.read(new URL(strUrl).openStream());
			List<Node> nodeList = doc.selectNodes("//alipay/*");
			for (Node node : nodeList)
			{
				// 截取部分不需要解析的信息
				if (node.getName().equals("is_success") && node.getText().equals("T"))
				{
					// 判断是否有成功标示
					List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
					for (Node node1 : nodeList1)
					{
						buf1.append(node1.getText());
					}
				}
			}
		}
		catch (Exception e)
		{
			logger.error("支付宝防钓鱼接口调用异常", e);
		}
		return buf1.toString();
	}

	/**
	 * 功能：把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * @param params 需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	private static String createLinkString(Map<String, String> params)
	{
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		String prestr = "";
		for (int i = 0; i < keys.size(); i++)
		{
			String key = (String) keys.get(i);
			String value = (String) params.get(key);
			if (null == value || value.equals(""))
			{
				continue;
			}
			if (i == keys.size() - 1)
			{// 拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			}
			else
			{
				prestr = prestr + key + "=" + value + "&";
			}
		}
		return prestr;
	}

	/**
	 * *功能：获取远程服务器ATN结果
	 * @param urlvalue 指定URL路径地址
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true 返回正确信息 false
	 *         请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	private String checkUrl(String urlvalue)
	{
		String inputLine = "";
		try
		{
			URL url = new URL(urlvalue);
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
			inputLine = in.readLine().toString();
		}
		catch (Exception e)
		{
			logger.error("查询支付返回信息真实性时发生异常", e);
		}
		return inputLine;
	}
}
